if SERVER then
    AddCSLuaFile()
end

DEFINE_BASECLASS("weapon_tttbase")

SWEP.HoldType = "revolver"

if CLIENT then
    SWEP.PrintName = "flare_name"
    SWEP.Slot = 6

    SWEP.ViewModelFOV = 54
    SWEP.ViewModelFlip = false

    SWEP.EquipMenuData = {
        type = "item_weapon",
        desc = "flare_desc",
    }

    SWEP.Icon = "vgui/ttt/icon_flare"
end

SWEP.Base = "weapon_tttbase"

-- if I run out of ammo types, this weapon is one I could move to a custom ammo
-- handling strategy, because you never need to pick up ammo for it
SWEP.Primary.Ammo = "AR2AltFire"
SWEP.Primary.Recoil = 4
SWEP.Primary.Damage = 7
SWEP.Primary.Delay = 1.0
SWEP.Primary.Cone = 0.01
SWEP.Primary.ClipSize = 4
SWEP.Primary.Automatic = false
SWEP.Primary.DefaultClip = 4
SWEP.Primary.ClipMax = 4
SWEP.Primary.Sound = Sound("Weapon_USP.SilencedShot")

SWEP.Kind = WEAPON_EQUIP
SWEP.CanBuy = { ROLE_TRAITOR } -- only traitors can buy
SWEP.LimitedStock = true -- only buyable once
SWEP.WeaponID = AMMO_FLARE

SWEP.builtin = true

SWEP.Tracer = "AR2Tracer"

SWEP.UseHands = true
SWEP.ViewModel = Model("models/weapons/c_357.mdl")
SWEP.WorldModel = Model("models/weapons/w_357.mdl")

if CLIENT then
    ---
    -- @ignore
    function SWEP:Initialize()
        self:AddTTT2HUDHelp("flaregun_help_primary")

        return BaseClass.Initialize(self)
    end
end

local function RunIgniteTimer(ent, timer_name)
    if IsValid(ent) and ent:IsOnFire() then
        if ent:WaterLevel() > 0 then
            ent:Extinguish()
        elseif CurTime() > ent.burn_destroy then
            ent:SetNotSolid(true)
            ent:Remove()
        else
            -- keep on burning
            return
        end
    end

    timer.Remove(timer_name) -- stop running timer
end

local SendScorches

if CLIENT then
    -- @ignore
    local function ReceiveScorches()
        local ent = net.ReadEntity()
        local num = net.ReadUInt(8)
        for i = 1, num do
            util.PaintDown(net.ReadVector(), "FadingScorch", ent)
        end

        if IsValid(ent) then
            util.PaintDown(ent:LocalToWorld(ent:OBBCenter()), "Scorch", ent)
        end
    end
    net.Receive("TTT_FlareScorch", ReceiveScorches)
else
    -- it's sad that decals are so unreliable when drawn serverside, failing to
    -- draw more often than they work, that I have to do this
    SendScorches = function(ent, tbl)
        net.Start("TTT_FlareScorch")
        net.WriteEntity(ent)
        net.WriteUInt(#tbl, 8)
        for _, p in ipairs(tbl) do
            net.WriteVector(p)
        end
        net.Broadcast()
    end
end

local function ScorchUnderRagdoll(ent)
    if SERVER then
        local postbl = {}
        -- small scorches under limbs
        for i = 0, ent:GetPhysicsObjectCount() - 1 do
            local subphys = ent:GetPhysicsObjectNum(i)
            if IsValid(subphys) then
                local pos = subphys:GetPos()
                util.PaintDown(pos, "FadingScorch", ent)

                table.insert(postbl, pos)
            end
        end

        SendScorches(ent, postbl)
    end

    -- big scorch at center
    local mid = ent:LocalToWorld(ent:OBBCenter())
    mid.z = mid.z + 25
    util.PaintDown(mid, "Scorch", ent)
end

---
-- @param Entity att
-- @param table path
-- @param CTakeDamageInfo dmginfo
-- @realm shared
function IgniteTarget(att, path, dmginfo)
    local ent = path.Entity

    if not IsValid(ent) then
        return
    end

    if CLIENT and IsFirstTimePredicted() then
        if ent:IsPlayerRagdoll() then
            ScorchUnderRagdoll(ent)
        end
        return
    end

    if SERVER then
        local dur = ent:IsPlayer() and 5 or 10

        -- disallow if prep or post round
        if ent:IsPlayer() and (not GAMEMODE:AllowPVP()) then
            return
        end

        ent:Ignite(dur, 100)

        ent.ignite_info = { att = dmginfo:GetAttacker(), infl = dmginfo:GetInflictor() }

        if ent:IsPlayer() then
            timer.Simple(dur + 0.1, function()
                if IsValid(ent) then
                    ent.ignite_info = nil
                end
            end)
        elseif ent:IsPlayerRagdoll() then
            ScorchUnderRagdoll(ent)

            local burn_time = 6
            local tname = Format("ragburn_%d_%d", ent:EntIndex(), math.ceil(CurTime()))

            ent.burn_destroy = CurTime() + burn_time

            timer.Create(
                tname,
                0.1,
                math.ceil(1 + burn_time / 0.1), -- upper limit, failsafe
                function()
                    RunIgniteTimer(ent, tname)
                end
            )
        end
    end
end

---
-- @ignore
function SWEP:ShootFlare()
    local cone = self.Primary.Cone
    local bullet = {}
    bullet.Num = 1
    bullet.Src = self:GetOwner():GetShootPos()
    bullet.Dir = self:GetOwner():GetAimVector()
    bullet.Spread = Vector(cone, cone, 0)
    bullet.Tracer = 1
    bullet.Force = 2
    bullet.Damage = self.Primary.Damage
    bullet.TracerName = self.Tracer
    bullet.Callback = IgniteTarget

    self:GetOwner():FireBullets(bullet)
end

---
-- @ignore
function SWEP:PrimaryAttack()
    self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)

    if not self:CanPrimaryAttack() then
        return
    end

    self:EmitSound(self.Primary.Sound)

    self:SendWeaponAnim(ACT_VM_PRIMARYATTACK)

    self:ShootFlare()

    self:TakePrimaryAmmo(1)

    local owner = self:GetOwner()

    if IsValid(owner) then
        owner:SetAnimation(PLAYER_ATTACK1)

        owner:ViewPunch(
            Angle(
                math.Rand(-0.2, -0.1) * self.Primary.Recoil,
                math.Rand(-0.1, 0.1) * self.Primary.Recoil,
                0
            )
        )
    end

    if (game.SinglePlayer() and SERVER) or CLIENT then
        self:SetNWFloat("LastShootTime", CurTime())
    end
end

---
-- @ignore
function SWEP:SecondaryAttack() end
